package team240;

import battlecode.common.*;
import java.util.ArrayList;

public class Archon extends AirRobot{
	
	RobotController myRC;
	RobotType toSpawn;
	// Units that should be following the archon
	private ArrayList<GroundRobot> combatUnits;
	// Minimum energon needed before archon can search for enemies
	final double minEnergonSearch = 30.0;
	
	Direction myHeading;
	MapLocation myTargetLocation,home;
	boolean general,harvester,defender,foundDeposit,onDeposit,locationAssessed;
	final int minimumNumberBlocksRequiredForHome = 20;
	final double idealNumberWorkers = 5;
	final double safeEnergonTransferLevel = 20;
	
	public Archon(RobotController inRC) {
		super(inRC);
		myRC = inRC;
		try {
			combatUnits = new ArrayList<GroundRobot>();
			onDeposit = false;
			toSpawn = null;
			foundDeposit = false;
			locationAssessed = false;
			home = myRC.getLocation();
			MapLocation[] archonLocations = myRC.senseAlliedArchons();
			if (archonLocations[0] == myRC.getLocation()) {
				general = true;
				harvester = false;
				defender = false;
				Direction broadcastHeading = myRC.senseDirectionToUnownedFluxDeposit();
				myHeading = broadcastHeading;
				Message openingDirection = new Message();
				String[] messageStrings = new String[1];
				messageStrings[0] = broadcastHeading.toString();
				int[] messageInts = new int[2];
				messageInts[0] = 2;
				messageInts[1] = Clock.getRoundNum();
				openingDirection.ints = messageInts;
				openingDirection.strings = messageStrings;
				myRC.broadcast(openingDirection);
			}
			else if (archonLocations[5] == myRC.getLocation()) {
				general = false;
				harvester = true;
				defender = false;
			}
			else if (archonLocations[4] == myRC.getLocation()) {
				general = false;
				harvester = false;
				defender = true;
			}
			else {
				general = false;
				harvester = false;
				defender = false;
			}
			myStatus = Status.FINDHOME;
			myRC.yield();
		} catch(Exception e) {
			System.out.println("caught exception:");
			e.printStackTrace();
		}
	}
	
	public void runArchon() {
		//main run method for the archon
		try {
			while (true) {
				//System.out.println(myStatus);
				switch(myStatus) {
				case SEARCH: search(); break;
				case COMBAT: combat(); break;
				case MINE: mine(); break;
				case TRAVEL: travel(); break;
				case DEFEND: defend(); break;
				case FINDHOME: findhome(); break;
				default: //error unrecognized status
				}
			}
		}catch(Exception e) {
			System.out.println("caught exception:");
			e.printStackTrace();
		}
	}
	public void findhome() {
		try {
			if (messagesDidNotChangeStatus()) {
				Robot enemySighted = findNearestEnemy(myRC.senseNearbyAirRobots(),myRC.senseNearbyGroundRobots());
				if (enemySighted == null) {
					if (myRC.isMovementActive()) {
						if (myRC.getEnergonLevel() > 70) {
							MapLocation targetLocation = myRC.getLocation().add(myRC.getDirection());
							if (myRC.senseGroundRobotAtLocation(targetLocation)==null && myRC.senseTerrainTile(targetLocation).getType()==TerrainTile.TerrainType.LAND) {
								myRC.spawn(RobotType.WORKER);
							}
						}
					}
					else {
						if (general) {
							if (foundDeposit) {
								if (onDeposit) {
									if (locationAssessed) {
										if (myRC.senseDirectionToOwnedFluxDeposit() == Direction.OMNI) {
											myRC.setDirection(myRC.getDirection().rotateRight());
										}
										else {
											myHeading = myRC.senseDirectionToUnownedFluxDeposit();
											Message newDirection = new Message();
											String[] messageStrings = new String[1];
											messageStrings[0] = myHeading.toString();
											newDirection.strings = messageStrings;
											int[] messageInts = new int[2];
											messageInts[0] = 2;
											messageInts[1] = Clock.getRoundNum();
											newDirection.ints = messageInts;
											myRC.broadcast(newDirection);
											foundDeposit = false;
											onDeposit = false;
											locationAssessed = false;
											moveInDirection(myHeading);
										}
									}
									else {
										int totalBlocks = 0;
										MapLocation[] blockLocations = myRC.senseNearbyBlocks();
										for (MapLocation x: blockLocations) {
											totalBlocks += myRC.senseNumBlocksAtLocation(x);
										}
										if (totalBlocks > minimumNumberBlocksRequiredForHome) {
											//add check to see if the squares adjacent to the flux deposit are land
											home = myTargetLocation;
											Message foundHome = new Message();
											MapLocation[] messageLocations = new MapLocation[1];
											messageLocations[0] = myTargetLocation;
											foundHome.locations = messageLocations;
											int[] messageInts = new int[2];
											messageInts[0] = 4;
											messageInts[1] = Clock.getRoundNum();
											foundHome.ints = messageInts;
											myRC.broadcast(foundHome);
											foundDeposit = false;
											onDeposit = false;
											myStatus = Status.SEARCH;
										}
										else {
											locationAssessed = true;
										}
									}
								}
								else {
									if (myRC.getLocation() == myTargetLocation) {
										onDeposit = true;
									}
									else {
										moveTowardLocation(myTargetLocation);
									}
								}
							}
							else {
								FluxDeposit[] nearbyDeposits = myRC.senseNearbyFluxDeposits();
								if (nearbyDeposits.length == 0) {
									Direction desiredHeading = myRC.senseDirectionToUnownedFluxDeposit();
									if (myHeading != desiredHeading) {
										Message changeDirection = new Message();
										String[] messageStrings = new String[1];
										messageStrings[0] = desiredHeading.toString();
										int[] messageInts = new int[2];
										messageInts[0] = 2;
										messageInts[1] = Clock.getRoundNum();
										changeDirection.ints = messageInts;
										changeDirection.strings = messageStrings;
										myRC.broadcast(changeDirection);
										myHeading = desiredHeading;
									}
									moveInDirection(myHeading);
								}
								else {
									MapLocation targetDeposit;
									int numberDeposits = nearbyDeposits.length;
									if (numberDeposits == 1) {
										targetDeposit = myRC.senseFluxDepositInfo(nearbyDeposits[0]).location;
									}
									else {
										MapLocation[] depositLocations = new MapLocation[numberDeposits]; 
										for (int k=0;k<nearbyDeposits.length;k++) {
											depositLocations[k] = myRC.senseFluxDepositInfo(nearbyDeposits[k]).location;
										}
										targetDeposit = getClosestLocation(depositLocations);
									}
									Message depositRally = new Message();
									MapLocation[] messageLocations = new MapLocation[1];
									messageLocations[0] = targetDeposit.add(myHeading).add(myHeading);
									depositRally.locations = messageLocations;
									int[] messageInts = new int[2];
									messageInts[0] = 3;
									messageInts[1] = Clock.getRoundNum();
									depositRally.ints = messageInts;
									myRC.broadcast(depositRally);
									foundDeposit = true;
									myTargetLocation = targetDeposit;
								}
							}
						}
						else {
							if (myTargetLocation == null) {
								moveInDirection(myHeading);
							}
							else {
								moveTowardLocation(myTargetLocation);
							}
						}
					}
				}
				else {
					//code for enemy sighted
				}
			}
			myRC.yield();
		} catch(Exception e) {
			System.out.println("caught exception:");
			e.printStackTrace();
		}
	}
	public void search() {
		try {
				if ( !preparedForSearch() ) {
					if ( combatUnits.size() < 2 && myRC.getEnergonLevel() > 20 ) {
						//spawn a soldier and add soldier to list of combatUnits
					}
					myRC.yield();
				}
				else {
					ArrayList<Robot> enemies = scanForEnemies();
					if ( enemies.size() != 0 ) {
						if ( canWinFight( enemies.size() ) ) {
							myStatus = Status.COMBAT;
						}
						else {
							retreat();
						}
					}
					// No enemies were found so begin moving to unowned flux
					else {
						if ( myRC.senseFluxDepositAtLocation( myRC.getLocation() ) != null ) {
							myStatus = Status.MINE;
						}
						else {
							//Transfer energon to any troops that need it
							//Direction dirToClosestFlux = myRC.senseDirectionToUnownedFluxDeposit();
							//Move towards the unowned flux
							//If no unowned flux then 
						}
					}
				}
				myRC.yield();
		}
		catch ( Exception e ) {
			System.out.println("Caught an exception!");
			e.printStackTrace();
		}
		
	}
	
	/**
	 * See if archon is prepared to actually search for and fight enemies
	 * @return true - minimum requirements have been met
	 */
	private boolean preparedForSearch() {
		if ( myRC.getEnergonLevel() > minEnergonSearch && 
			combatUnits.size() >= 2) {
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * See if any enemies are in range
	 * @return list of all enemies in range
	 * @throws GameActionException 
	 */
	private ArrayList<Robot> scanForEnemies() throws GameActionException {
		ArrayList<Robot> enemies = new ArrayList<Robot>();
		
		Robot[] airBots = myRC.senseNearbyAirRobots();
		for ( Robot airBot : airBots ) {
			if ( myRC.canSenseObject( airBot ) ) {
				RobotInfo info = myRC.senseRobotInfo( airBot );
				if ( info.team.opponent() == myRC.getTeam() ) {
					enemies.add( airBot );
				}
					
			}
		}
		Robot[] groundBots = myRC.senseNearbyGroundRobots();
		for ( Robot groundBot : groundBots ) {
			if ( myRC.canSenseObject( groundBot ) ) {
				RobotInfo info = myRC.senseRobotInfo( groundBot );
				if ( info.team.opponent() == myRC.getTeam() )
					enemies.add( groundBot );
			}
		}
		return enemies;
	}
	
	/**
	 * Evaluate if entering combat is a good idea
	 * @param numEnemies - the number of enemies detected by archon
	 * @return true - if victory seems probable
	 */
	private boolean canWinFight( int numEnemies) {
		if ( numEnemies <= combatUnits.size() ) {
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 *  Get away from enemies
	 */
	private void retreat() {
		//TODO How do we want to move away from enemies?
	}
	
	public void combat() {
		try {
			//helper methods here
			//for example: lookForEnemy()
			//transferEnergon()
			myRC.yield();
        } catch(Exception e) {
           System.out.println("caught exception:");
           e.printStackTrace();
        }
	}
	
	public void mine() {
		try {
			if (messagesDidNotChangeStatus()) {
				int numberWorkers = 0;
				Robot nearestEnemy = null;
				Robot[] groundRobots = myRC.senseNearbyGroundRobots();
				Robot[] airRobots = myRC.senseNearbyAirRobots();
				Team myTeam = myRC.getTeam();
				MapLocation myLocation = myRC.getLocation();
				int closest = 100;
				for (Robot x: groundRobots) {
					RobotInfo foreignRobot = myRC.senseRobotInfo(x);
					if (foreignRobot.team != myTeam) {
						int distanceToRobot = getDistanceSquared(myLocation,foreignRobot.location);
						if (distanceToRobot < closest) {
							nearestEnemy = x;
							closest = distanceToRobot;
						}
					}
					else {
						if (foreignRobot.location.isAdjacentTo(myLocation)) {
							transferEnergon(x);
						}
						if (foreignRobot.type == RobotType.WORKER) {
							numberWorkers++;
						}
					}
				}
				for (Robot y: airRobots) {
					RobotInfo foreignRobot = myRC.senseRobotInfo(y);
					if (foreignRobot.team != myTeam) {
						int distanceToRobot = getDistanceSquared(myLocation,foreignRobot.location);
						if (distanceToRobot < closest) {
							nearestEnemy = y;
							closest = distanceToRobot;
						}
					}
				}
				if (nearestEnemy == null) {
					if (numberWorkers < idealNumberWorkers) {
						if (myRC.getEventualEnergonLevel() > 50) {
							Direction myDirection = myRC.getDirection();
							MapLocation targetLocation = myRC.getLocation().add(myDirection);
							if (myRC.senseGroundRobotAtLocation(targetLocation)==null && myRC.senseTerrainTile(targetLocation).getType()==TerrainTile.TerrainType.LAND) {
								myRC.spawn(RobotType.WORKER);
							}
							else {
								if (!myRC.isMovementActive()) {
									myRC.setDirection(myDirection.rotateRight());
								}
							}
						}
					}
				}
				else {
					//enemy sighted code
				}
			}
			myRC.yield();
        }catch(Exception e) {
           System.out.println("caught exception:");
           e.printStackTrace();
        }
	}
	public String findDirectionToNearestAvailableBlock() {
		MapLocation myLocation = myRC.getLocation();
		int lowest = 100;
		MapLocation closest = null;
		MapLocation[] nearbyBlocks = myRC.senseNearbyBlocks();
		for (MapLocation x: nearbyBlocks) {
			if (!x.isAdjacentTo(myLocation)) {
				int blockDistance = getDistanceSquared(myLocation,x);
				if (blockDistance < lowest) {
					closest = x;
					lowest = blockDistance;
				}
			}
		}
		if (closest == null) {
			//find direction for the workers to explore in
			return myRC.getDirection().toString();
		}
		return getDirection(myLocation,closest).toString();
	}
	public void travel() {
		if (messagesDidNotChangeStatus()) {
			Robot enemySighted = findNearestEnemy(myRC.senseNearbyAirRobots(),myRC.senseNearbyGroundRobots());
			if (enemySighted == null) {
				if (!myRC.isMovementActive()) {
					if (myTargetLocation == null) {
						moveInDirection(myHeading);
					}
					else {
						moveTowardLocation(myTargetLocation);
					}
				}
			}
		}
		myRC.yield();
	}
	public void defend() {
		try {
			if (messagesDidNotChangeStatus()) {
				int numberWorkers = 0;
				int numberChannelers = 0;
				int numberCannons = 0;
				Robot nearestEnemy = null;
				Robot[] groundRobots = myRC.senseNearbyGroundRobots();
				Robot[] airRobots = myRC.senseNearbyAirRobots();
				Team myTeam = myRC.getTeam();
				MapLocation myLocation = myRC.getLocation();
				int closest = 100;
				MapLocation needsTransfer = myLocation;
				int lowestEnergon = 100;
				for (Robot x: groundRobots) {
					RobotInfo foreignRobot = myRC.senseRobotInfo(x);
					if (foreignRobot.team != myTeam) {
						int distanceToRobot = getDistanceSquared(myLocation,foreignRobot.location);
						if (distanceToRobot < closest) {
							nearestEnemy = x;
							closest = distanceToRobot;
						}
					}
					else {
						double robotEnergon = foreignRobot.eventualEnergon;
						if (robotEnergon<lowestEnergon && foreignRobot.type!=RobotType.ARCHON) {
							needsTransfer = foreignRobot.location;
						}
						if (foreignRobot.location.isAdjacentTo(myLocation)) {
							transferEnergon(x);
						}
						if (foreignRobot.type == RobotType.WORKER) {
							numberWorkers++;
						}
						else if (foreignRobot.type == RobotType.CHANNELER) {
							numberChannelers++;
						}
						else if (foreignRobot.type == RobotType.CANNON) {
							numberCannons++;
						}
					}
				}
				for (Robot y: airRobots) {
					RobotInfo foreignRobot = myRC.senseRobotInfo(y);
					if (foreignRobot.team != myTeam) {
						int distanceToRobot = getDistanceSquared(myLocation,foreignRobot.location);
						if (distanceToRobot < closest) {
							nearestEnemy = y;
							closest = distanceToRobot;
						}
					}
				}
				if (nearestEnemy == null) {
					if (numberCannons < 1) {
						if (myRC.getEventualEnergonLevel() > 50) {
							Direction myDirection = myRC.getDirection();
							if (myRC.senseGroundRobotAtLocation(myLocation.add(myDirection)) == null) {
								myRC.spawn(RobotType.CANNON);
							}
							else {
								if (!myRC.isMovementActive()) {
									myRC.setDirection(myDirection.rotateRight());
								}
							}
						}
					}
					else if (numberChannelers < 1) {
						if (myRC.getEventualEnergonLevel() > 50) {
							Direction myDirection = myRC.getDirection();
							MapLocation targetLocation = myRC.getLocation().add(myDirection);
							if (myRC.senseGroundRobotAtLocation(targetLocation)==null && myRC.senseTerrainTile(targetLocation).getType()==TerrainTile.TerrainType.LAND) {
								myRC.spawn(RobotType.CHANNELER);
							}
							else {
								if (!myRC.isMovementActive()) {
									myRC.setDirection(myDirection.rotateRight());
								}
							}
						}
					}
					else if (numberWorkers < idealNumberWorkers) {
						if (myRC.getEventualEnergonLevel() > 50) {
							Direction myDirection = myRC.getDirection();
							MapLocation targetLocation = myRC.getLocation().add(myDirection);
							if (myRC.senseGroundRobotAtLocation(targetLocation)==null && myRC.senseTerrainTile(targetLocation).getType()==TerrainTile.TerrainType.LAND) {
								myRC.spawn(RobotType.WORKER);
							}
							else {
								if (!myRC.isMovementActive()) {
									myRC.setDirection(myDirection.rotateRight());
								}
							}
						}
					}
					else {
						if (!myRC.isMovementActive()) {
							moveTowardLocation(needsTransfer);
						}
					}
				}
				else {
					//enemy sighted code
				}
			}
			myRC.yield();
        }catch(Exception e) {
           System.out.println("caught exception:");
           e.printStackTrace();
        }
	}
	public void transferEnergon(Robot target) {
		try {
			RobotInfo targetInfo = myRC.senseRobotInfo(target);
			double targetReserve = targetInfo.energonReserve;
			if (myRC.getEventualEnergonLevel()>safeEnergonTransferLevel && targetReserve<10) {
				myRC.transferEnergon(10-targetReserve, targetInfo.location, target.getRobotLevel());
			}
		} catch(Exception e) {
			System.out.println("caught exception:");
			e.printStackTrace();
		}
	}
	public boolean messagesDidNotChangeStatus() {
		//message int index: 1-enemy sighted 2-change direction 3-change location 4-found home 5-workers wanted direction toward blocks
		try {
			boolean statusNotChanged = true;
			Message[] newMessages = myRC.getAllMessages();
			for (Message k: newMessages) {
				if (messageValidated(k)) {
					switch (k.ints[0]) {
					case 2: myHeading = Direction.valueOf(k.strings[0]);
						myTargetLocation = null; break;
					case 3: myTargetLocation = k.locations[0];
						myHeading = null; break;
					case 4: home = k.locations[0];
						if (defender) {
							myStatus = Status.DEFEND;
							myTargetLocation = null;
							myHeading = null;
						}
						else if (harvester) {
							myStatus = Status.TRAVEL;
							myTargetLocation = home;
							myHeading = null;
						}
						else {
							myStatus = Status.SEARCH;
							myTargetLocation = null;
							myHeading = null;
						}
						statusNotChanged = false; break;
					case 5: //be careful, mining archons can no longer send messages. If they need to then you have to check for queued messages
						if (harvester) {
							Message lookInDirection = new Message();
							String[] messageStrings = new String[1];
							messageStrings[0] = findDirectionToNearestAvailableBlock();
							int[] messageInts = new int[2];
							messageInts[0] = 6;
							messageInts[1] = Clock.getRoundNum();
							lookInDirection.ints = messageInts;
							lookInDirection.strings = messageStrings;
							myRC.broadcast(lookInDirection);
						}
						break;
					default: //error unrecognized integer message indicator
					}
				}
			}
			return statusNotChanged;
		}catch(Exception e) {
			System.out.println("caught exception:");
			e.printStackTrace();
			return true;
		}
	}
}